package org.dei.perla.rockfall.utils;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.dei.perla.parser.expressions.ConstantBlob;
import org.dei.perla.parser.expressions.ConstantCastException;
import org.dei.perla.parser.expressions.ConstantTimestamp;
import org.dei.perla.sys.device.fpc.dspic.DSPicFPC;
import org.dei.perla.sys.query.llqruntime.datastructures.Record;
import org.dei.perla.utils.pipe.Pipe;
import org.dei.perla.utils.waiter.Waitable;
import org.dei.perla.utils.waiter.Waiter;

public class DatabaseDump implements Runnable {
	
	private Connection conn = null;
	private PreparedStatement pStmBibiContinuous;
	private PreparedStatement pStmBibiOnEvent;
	private PreparedStatement pStmAttitude;
	private PreparedStatement pStmTemperature;
	private PreparedStatement pStmCrackmeter;
	
	private final Waiter<Pipe<Record>> pRecordWaiter;
	
	/**
	 * Costruisce un nuovo oggetto DatabaseDump
	 * @param parInPipe pipe sulla quale leggere i dati
	 */
	public DatabaseDump(Pipe<Record> parInPipe) {
		pRecordWaiter = new Waiter<Pipe<Record>>();
		pRecordWaiter.start();
		pRecordWaiter.addWaitable(parInPipe.getWaitable());
				
		initializeDB();
	}

	/**
	 * Inizializza la connessione verso MySQL
	 */
	private void initializeDB() {
		
		try {
			conn = DriverManager.getConnection("jdbc:mysql://localhost/perla?" + "user=perla&password=perla");
			
			// Inizializzo le varie query...
			pStmBibiOnEvent = conn.prepareStatement("INSERT INTO bibi " +
					"(ID_SENSORE, ID_NODO, TIMESTAMP, DATA, FLAGS, SAMPLES, MAGNITUDEX, MAGNITUDEY, " +
					"MAGNITUDEZ, PEAKX, PEAKY, PEAKZ, NOISEX, NOISEY, NOISEZ) " +
					"VALUES	(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
			pStmBibiContinuous = conn.prepareStatement("INSERT INTO bibi " +
					"(ID_SENSORE, ID_NODO, TIMESTAMP, DATA, ) VALUES	(?, ?, ?, ?)");
			pStmTemperature = conn.prepareStatement("INSERT INTO temperature " +
					"(ID_SENSORE, ID_NODO, TIMESTAMP, value) VALUES (?, ?, ?, ?)");
			pStmAttitude = conn.prepareStatement("INSERT INTO attitude " +
					"(ID_SENSORE, ID_NODO, TIMESTAMP, attitudeX, attitudeY, attitudeZ) VALUES (?, ?, ?, ?, ?, ?)");
			pStmCrackmeter = conn.prepareStatement("INSERT INTO crackmeter " +
					"(ID_SENSORE, ID_NODO, TIMESTAMP, crackmeter) VALUES (?, ?, ?, ?)");
		
		} catch (SQLException ex) {
			// Gestione errori
			System.out.println("SQLException: " + ex.getMessage());
			System.out.println("SQLState: " + ex.getSQLState());
			System.out.println("VendorError: " + ex.getErrorCode());
		}
	}
	
	/**
	 * Chiudo la connessione con il DB
	 */
	private void closeDB() {
		if (conn != null){
			try {
				conn.close();
				pStmAttitude.close();
				pStmBibiContinuous.close();
				pStmCrackmeter.close();
				pStmTemperature.close();
			} catch(SQLException e) {
				System.out.println("Errore durante la chiusura della connessione al database");
			}
		}
	}
	
	/**
	 * Metodo helper per eseguire INSERT del record
	 * @param tRecord il record da inserire nel database
	 */
	private void sendToDB(Record tRecord) {
		if (conn == null) {
			System.out.println("Impossibile memorizzare su DB, errore durante la connessione al DB");
			return;
		}
		
		try {
			if (tRecord.getField("ACQUISITION_TYPE").getValueInt() == DSPicFPC.CONTINUOUS) {
				pStmBibiContinuous.setString(1, "BIBI_" + tRecord.getField("ID_NODO").getValueID());
				pStmBibiContinuous.setString(2, tRecord.getField("ID_NODO").getValueID());
				pStmBibiContinuous.setLong(3, ((ConstantTimestamp)tRecord.getField("TIMESTAMP")).getValueTimestamp());
				pStmBibiContinuous.setBytes(4, ((ConstantBlob)tRecord.getField("DATA")).getValueBlob());
				pStmBibiContinuous.executeUpdate();
			} else if (tRecord.getField("ACQUISITION_TYPE").getValueInt() == DSPicFPC.ON_EVENT) {
				pStmBibiOnEvent.setString(1, "BIBI_" + tRecord.getField("ID_NODO").getValueID());
				pStmBibiOnEvent.setString(2, tRecord.getField("ID_NODO").getValueID());
				pStmBibiOnEvent.setLong(3, ((ConstantTimestamp)tRecord.getField("TIMESTAMP")).getValueTimestamp());
				pStmBibiOnEvent.setBytes(4, ((ConstantBlob)tRecord.getField("DATA")).getValueBlob());
				pStmBibiOnEvent.setInt(5, tRecord.getField("FLAGS").getValueInt());
				pStmBibiOnEvent.setInt(6, tRecord.getField("SAMPLES").getValueInt());
				pStmBibiOnEvent.setInt(7, tRecord.getField("MAGNITUDEX").getValueInt());
				pStmBibiOnEvent.setInt(8, tRecord.getField("MAGNITUDEY").getValueInt());
				pStmBibiOnEvent.setInt(9, tRecord.getField("MAGNITUDEZ").getValueInt());
				pStmBibiOnEvent.setInt(10, tRecord.getField("PEAKX").getValueInt());
				pStmBibiOnEvent.setInt(11, tRecord.getField("PEAKY").getValueInt());
				pStmBibiOnEvent.setInt(12, tRecord.getField("PEAKZ").getValueInt());
				pStmBibiOnEvent.setInt(13, tRecord.getField("NOISEX").getValueInt());
				pStmBibiOnEvent.setInt(14, tRecord.getField("NOISEX").getValueInt());
				pStmBibiOnEvent.setInt(15, tRecord.getField("NOISEX").getValueInt());
				pStmBibiOnEvent.executeUpdate();
			}
			
			pStmCrackmeter.setString(1, "CRACKMETER1_" + tRecord.getField("ID_NODO").getValueID());
			pStmCrackmeter.setString(2, tRecord.getField("ID_NODO").getValueID());
			pStmCrackmeter.setLong(3, ((ConstantTimestamp)tRecord.getField("TIMESTAMP")).getValueTimestamp());
			pStmCrackmeter.setInt(4, tRecord.getField("CRACKMETER1").getValueInt());
			pStmCrackmeter.executeUpdate();
			
			pStmCrackmeter.setString(1, "CRACKMETER2_" + tRecord.getField("ID_NODO").getValueID());
			pStmCrackmeter.setString(2, tRecord.getField("ID_NODO").getValueID());
			pStmCrackmeter.setLong(3, ((ConstantTimestamp)tRecord.getField("TIMESTAMP")).getValueTimestamp());
			pStmCrackmeter.setInt(4, tRecord.getField("CRACKMETER2").getValueInt());
			pStmCrackmeter.executeUpdate();
			
			pStmTemperature.setString(1, "TEMPERATURE_" + tRecord.getField("ID_NODO").getValueID());
			pStmTemperature.setString(2, tRecord.getField("ID_NODO").getValueID());
			pStmTemperature.setLong(3, ((ConstantTimestamp)tRecord.getField("TIMESTAMP")).getValueTimestamp());
			pStmTemperature.setInt(4, tRecord.getField("TEMPERATURE").getValueInt());
			pStmTemperature.executeUpdate();
			
			pStmAttitude.setString(1, "ATTITUDE_" + tRecord.getField("ID_NODO").getValueID());
			pStmAttitude.setString(2, tRecord.getField("ID_NODO").getValueID());
			pStmAttitude.setLong(3, ((ConstantTimestamp)tRecord.getField("TIMESTAMP")).getValueTimestamp());
			pStmAttitude.setInt(4, tRecord.getField("ATTITUDEX").getValueInt());
			pStmAttitude.setInt(5, tRecord.getField("ATTITUDEY").getValueInt());
			pStmAttitude.setInt(6, tRecord.getField("ATTITUDEZ").getValueInt());
			pStmAttitude.executeUpdate();
			
			System.out.println("Record memorizzato su DB");
			
		} catch (SQLException e) {
			System.out.println("Errore durante l'immissione dei dati nel DB: " + e.getMessage());
		} catch (ConstantCastException e) {
			System.out.println("Errore durante l'immissione dei dati nel DB, " +
					"cast Constant non supportato: " + e.getMessage());
		}
	}

	public void run() {
		Waitable<Pipe<Record>> waitable;
		Record record;
		
		while(true) {
			waitable = pRecordWaiter.waitNext();
			record = waitable.getParentComponent().dequeue();
			if (record != null) {
				// Memorizzo il record sul database
				sendToDB(record);
			} else {
				// Killpacket ricevuto, chiudo la connessione con il DB,
				// termino le pipe, termino il processo
				pRecordWaiter.removeWaitable(waitable);
				waitable.getParentComponent().stop();
				pRecordWaiter.stop();
				this.closeDB();
				break;
			}
		}
		System.out.println("DBDump terminated");
	}
}
